package ${package}.${rootArtifactId}.common.exception.handler;

import com.google.common.base.Joiner;
import ${package}.api.common.model.RemoteResult;
import ${package}.${rootArtifactId}.common.exception.BusinessException;
import ${package}.${rootArtifactId}.common.exception.ValidateException;
import io.lettuce.core.RedisException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 公共异常拦截处理
 *
 * @author weidong.lin
 * @date 2021/7/8 11:06
 */
@RestControllerAdvice
public class WebExceptionHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(WebExceptionHandler.class);

    /**
     * 自定义业务异常拦截
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(BusinessException.class)
    public RemoteResult handleBusinessException(BusinessException e) {
        LOGGER.error("【异常】业务逻辑异常:{}", e.getMessage());
        return RemoteResult.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
    }

    /**
     * 校验异常
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(ValidateException.class)
    public RemoteResult handleValidateException(ValidateException e) {
        LOGGER.error("【异常】校验异常:{}", e.getMessage());
        return RemoteResult.failure(HttpStatus.FORBIDDEN.value(), e.getMessage());
    }

    /**
     * 异常拦截
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(Exception.class)
    public RemoteResult handleException(Exception e) {
        LOGGER.error("【异常】系统异常：{}", e);
        return RemoteResult.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "系统异常");
    }

    /**
     * 运行时异常拦截  500
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(RuntimeException.class)
    public RemoteResult handleRuntimeException(RuntimeException e) {
        LOGGER.error("【异常】系统运行异常：{}", e);
        return RemoteResult.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "系统运行异常");
    }

    /**
     * 空指针异常拦截
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(NullPointerException.class)
    public RemoteResult handleNullPointerException(NullPointerException e) {
        LOGGER.error("【异常】空指针异常", e);
        return RemoteResult.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "空指针异常");
    }

    /**
     * 请求参数不合法 400 - Bad Request
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public RemoteResult handleIllegalArgumentException(IllegalArgumentException e) {
        LOGGER.error("【异常】请求参数不合法", e);
        return RemoteResult.error(HttpStatus.BAD_REQUEST.value(), "请求参数不合法");
    }

    /**
     * 缺少请求参数 400 - Bad Request
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public RemoteResult handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        LOGGER.error("【异常】缺少请求参数", e);
        return RemoteResult.error(HttpStatus.BAD_REQUEST.value(), "缺少请求参数");
    }

    /**
     * 参数解析异常 400 - Bad Request
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public RemoteResult handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        LOGGER.error("【异常】参数不可用异常", e);
        return RemoteResult.error(HttpStatus.BAD_REQUEST.value(), "参数不可用");
    }

    /**
     * 数据库异常拦截
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(SQLException.class)
    public RemoteResult handleSqlException(SQLException e) {
        LOGGER.error("【异常】数据库操作异常", e);
        return RemoteResult.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "数据库操作异常");
    }

    /**
     * redis访问异常拦截
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(RedisException.class)
    public RemoteResult handleRedisException(RedisException e) {
        LOGGER.error("【异常】redis访问异常", e);
        return RemoteResult.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "redis访问异常");
    }

    /**
     * 页面未找到异常
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(value = NoHandlerFoundException.class)
    public RemoteResult handlerNoHandlerFoundException(NoHandlerFoundException e) {
        LOGGER.error("【异常】页面请求异常", e);
        return RemoteResult.error(HttpStatus.NOT_FOUND.value(), "页面请求异常");
    }

    /**
     * 不支持当前请求方法 405 - Method Not Allowed
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public RemoteResult handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        LOGGER.error("【异常】不支持当前请求方法", e);
        return RemoteResult.error(HttpStatus.METHOD_NOT_ALLOWED.value(), "不支持当前请求方法");
    }

    /**
     * 不支持当前媒体类型 415 - Unsupported Media Type
     *
     * @param e
     * @return {@link RemoteResult}
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public RemoteResult handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        LOGGER.error("【异常】不支持当前媒体类型", e);
        return RemoteResult.error(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), "不支持当前请求方法");
    }

    /**
     * 参数绑定异常
     *
     * @param exception
     * @return {@link RemoteResult}
     */
    @ExceptionHandler({MethodArgumentNotValidException.class, ConstraintViolationException.class, BindException.class})
    public RemoteResult handleViolationException(Exception exception) {
        BindingResult bindRemoteResult = null;
        List<String> list = new ArrayList<String>();
        if (exception instanceof MethodArgumentNotValidException) {
            bindRemoteResult = ((MethodArgumentNotValidException) exception).getBindingResult();
        } else if (exception instanceof ConstraintViolationException) {
            Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) exception)
                    .getConstraintViolations();
            for (ConstraintViolation<?> violation : constraintViolations) {
                list.add(violation.getMessage());
            }
        } else if (exception instanceof BindException) {
            bindRemoteResult = ((BindException) exception).getBindingResult();
        }
        if (bindRemoteResult != null) {
            for (ObjectError objectError : bindRemoteResult.getAllErrors()) {
                list.add(objectError.getDefaultMessage());
            }
        }
        return RemoteResult.failure(HttpStatus.BAD_REQUEST.value(), Joiner.on(",").skipNulls().join(list));
    }

}
